/*
	ACDM_mod.c

	contains the kernel module functionality entry points for the ACDM

	Author: Aidan Goddard 23/10/13
*/

// includes
#include"../../kernel/headers/types.h"
#include"../../klib/headers/klib_message_passing.h"
#include"../../klib/headers/klib_system_resources.h"
#include"../../klib/headers/klib_proc_threads.h"
#include"ACDM_mod.h"
#include"ACDM_window.h"
#include"ACDM_keyboard.h"



///////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// external functions

extern void ACDMSpinEnter(uint64_t *lock);
extern void ACDMSpinLeave(uint64_t *lock);

///////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// internal queue functions

// 256 byte message structure
struct MESSAGE
{
	uint8_t msg[256];
};

// message queue & controls
static uint32_t q_base = 0;
static uint32_t q_length = 0;
static struct MESSAGE m_queue[32];
static uint64_t q_lock = 0;

// module message send function
// returns 0 on success, non-zero on error
// returns 1 for buffer is full, try again
static uint32_t SendMessage(uint8_t *msg)
{
	// get lock on the queue
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	ACDMSpinEnter(&q_lock);

	// the error value. default to buffer full
	uint32_t to_return = 1;

	// check if the buffer is non-full
	if(q_length < 32)
	{
		// buffer is not full, continue
		// get buffer index to use & increase the length
		uint32_t q_index = (q_base + q_length) % 32;
		q_length++;

		// copy the message into the queue
		uint64_t *src_ptr = (uint64_t*)msg;
		uint64_t *dst_ptr = (uint64_t*)m_queue[q_index].msg;
		int i;
		for(i = 0; i < 32; i++)
		{
			dst_ptr[i] = src_ptr[i];
		}

		// set error value to 0
		to_return = 0;
	}

	// finished, unlock and return;
	ACDMSpinLeave(&q_lock);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");
	return to_return;
}

// function to check if messages exist in the queue
// returns the number of messages in the queue
uint32_t ACDMGetQueueLength(void)
{
	// get lock on the queue
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	ACDMSpinEnter(&q_lock);

	// get the return value
	uint32_t to_return = q_length;

	// finished, unlock and return;
	ACDMSpinLeave(&q_lock);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");
	return to_return;
}

// function to read a message from the queue
// returns 0 on success, 2 for queue was empty
uint32_t ACDMReadMessage(uint8_t *dest_buffer)
{
	// get lock on the queue
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	ACDMSpinEnter(&q_lock);

	// the error value. default to buffer empty
	uint32_t to_return = 2;

	// check if the buffer is non-empty
	if(q_length > 0)
	{
		// buffer is not empty, continue
		// get index and increase the start pointer while decreasing length
		uint32_t q_index = q_base;
		q_base++;
		q_length--;

		// copy the message into the dest buffer
		uint64_t *src_ptr = (uint64_t*)m_queue[q_index].msg;
		uint64_t *dst_ptr = (uint64_t*)dest_buffer;
		int i;
		for(i = 0; i < 32; i++)
		{
			dst_ptr[i] = src_ptr[i];
		}

		// set error value to 0
		to_return = 0;
	}

	// finished, unlock and return;
	ACDMSpinLeave(&q_lock);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");
	return to_return;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// functions that send messages to the ACDM process

// queue lock structure
//static uint64_t ACDM_proc_lock = 0;
static uint64_t ACDM_log_lock = 0;
static uint64_t ACDM_mod_lock = 0;

// function to give a window focus
void ACDMGiveFocus(HWND hwnd)
{
	// send message to the ACDM proc
	uint8_t msg_buffer[256];
	uint64_t *b = (uint64_t*)msg_buffer;
	b[0] = 0;
	b[1] = hwnd;

	// send the message
	while(SendMessage(msg_buffer))
	{
		__KLIB__SleepThisThread(15000);
	}

	// finished
	return;
}

// function to send the login message to the ACDM process
void ACDMLogin(void)
{
	// send message to the ACDM proc
	uint8_t msg_buffer[256];
	uint64_t *b = (uint64_t*)msg_buffer;
	b[0] = 1;

	// send the message
	while(SendMessage(msg_buffer))
	{
		__KLIB__SleepThisThread(15000);
	}

	// finished
	return;
}

// function to tell a thread to wait until its associated window becomes active
void ACDMWindowWaitForFocus(HWND hwnd)
{
	// get the thread ID
	uint64_t this_TID = __KLIB__RCGetTID();

	// send message to the ACDM proc
	uint8_t msg_buffer[256];
	uint64_t *b = (uint64_t*)msg_buffer;
	b[0] = 2;
	b[1] = hwnd;
	b[2] = this_TID;

	// send the message
	while(SendMessage(msg_buffer))
	{
		__KLIB__SleepThisThread(15000);
	}

	// sleep
	__KLIB__WaitThisThread();
	return;
}

// function to print to the system log
void ACDMPrintLog(char *string, int string_length)
{
	// check the string length
	if(string_length > 246)
	{
		string_length = 246;
	}

	// copy the message to the buffer
	uint8_t buffer[256];
	int i;
	for(i = 0; i < 246 && i < string_length; i++)
	{
		buffer[i] = string[i];
	}
	buffer[246] = 0;
	buffer[247] = (uint8_t)string_length;

	// get TID
	uint64_t TID = __KLIB__RCGetTID();
	*((uint64_t*)&buffer[248]) = TID;

	// find the message channel and send the string
	char *key_string = "I AM THE LOG QUEUE";
	MSG_CHANNEL channel = __KLIB__MSGFindChannel(key_string, 122);
	if(channel == 0)
	{
		return;
	}
	while(__KLIB__MSGClientSendAsync(channel, buffer, 0xdeadbeefbabe0001) != MSG_SUCCESS)
	{
		__KLIB__SleepThisThread(15000);
	}

	// finished
	return;
}

// functions to show and hide a window
void ACDMWindowHide(HWND hwnd)
{
	// send message to the ACDM proc
	uint8_t msg_buffer[256];
	uint64_t *b = (uint64_t*)msg_buffer;
	b[0] = 4;
	b[1] = hwnd;

	// send the message
	while(SendMessage(msg_buffer))
	{
		__KLIB__SleepThisThread(15000);
	}

	// finished
	return;
}

void ACDMWindowShow(HWND hwnd)
{
	// send message to the ACDM proc
	uint8_t msg_buffer[256];
	uint64_t *b = (uint64_t*)msg_buffer;
	b[0] = 3;
	b[1] = hwnd;

	// send the message
	while(SendMessage(msg_buffer))
	{
		__KLIB__SleepThisThread(15000);
	}

	// finished
	return;
}


// window allocation & deallocation
HWND ACDMCreateStaticWindow(uint64_t PID, char *window_name, int name_count)
{
	// get lock on the system
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	ACDMSpinEnter(&ACDM_mod_lock);

	// create the window and grab the HWND
	HWND hwnd = WindowCreateStatic(PID, window_name, name_count);

	// unlock and return
	ACDMSpinLeave(&ACDM_mod_lock);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// finished
	return hwnd;
}

// window destroying
void ACDMDestroyWindow(HWND hwnd)
{
	// send message to the ACDM proc
	uint8_t msg_buffer[256];
	uint64_t *b = (uint64_t*)msg_buffer;
	b[0] = 5;
	b[1] = hwnd;

	// send the message
	while(SendMessage(msg_buffer))
	{
		__KLIB__SleepThisThread(15000);
	}

	// finished
	return;
}

void ACDMGetWindowsForProc(uint64_t PID, HWND *windows, uint32_t *total_windows)
{
	// set defaults
	*total_windows = 0;

	// get lock on the system
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	ACDMSpinEnter(&ACDM_mod_lock);

	// get the windows
	WindowGetForProcess(PID, windows, total_windows);

	// unlock and return
	ACDMSpinLeave(&ACDM_mod_lock);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// finished
	return;
}

void ACDMDestroyWindowsForProc(uint64_t PID)
{
	// first get list of HWNDs for this proc
	HWND windows[512];
	uint32_t total_windows;
	ACDMGetWindowsForProc(PID, windows, &total_windows);

	// then for each one, delete it
	int i;
	for(i = 0; i < total_windows; i++)
	{
		ACDMDestroyWindow(windows[i]);
	}

	// finished
	return;
}

// window control functions
void ACDMWindowUpdate(HWND hwnd)
{
	// update the window
	WindowUpdate(hwnd);

	// finished
	return;
}


void ACDMWindowChangeName(HWND hwnd, char *window_name, int name_count)
{
	// get lock on the system
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	ACDMSpinEnter(&ACDM_mod_lock);

	// update the window name
	WindowChangeName(hwnd, window_name, name_count);

	// unlock and return
	ACDMSpinLeave(&ACDM_mod_lock);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// finished
	return;
}

uint64_t ACDMWindowGetBufferAddress(HWND hwnd)
{
	// get lock on the system
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("cli");
	__asm__ __volatile__("":::"memory");
	ACDMSpinEnter(&ACDM_mod_lock);

	// get the buffer address
	uint64_t to_return = WindowGetStaticBufferAddress(hwnd);

	// unlock and return
	ACDMSpinLeave(&ACDM_mod_lock);
	__asm__ __volatile__("":::"memory");
	__asm__ __volatile__("sti");
	__asm__ __volatile__("":::"memory");

	// finished
	return to_return;
}


// keyboard access functions
uint8_t ACDMWindowWaitForChar(HWND hwnd)
{
	// call the window function
	uint8_t to_return = WindowGetASCIIChar(hwnd);
	return to_return;
}

void ACDMWindowClearChar(HWND hwnd)
{
	// call the window function
	WindowClearASCIIChar(hwnd);
	return;
}

void ACDMWindowGetKeystate(HWND hwnd, uint8_t *keystate)
{
	// call the window function
	WindowGetKeyState(hwnd, keystate);
	return;
}




